#include "AppDatabaseTest.h"

#define RGB_CANNY
//#define DO_BLUR




#define MAX_DISTANCE 5




#include "abliefern/Segment.h"
#include "abliefern/VanishingPoint.h"
#include "abliefern/Point2f.h"

#include <vector>
#include <algorithm>


using namespace abliefern;


bool compareVP(VanishingPoint* the1, VanishingPoint* the2){
	return (the1->supportCount > the2->supportCount);
}


static int callback(void *NotUsed, int argc, char **argv, char **azColName){
	int i;
	for(i=0; i<argc; i++){
		printf("%s = %s\n", azColName[i], argv[i] ? argv[i] : "NULL");
	}
	printf("\n");
	return 0;
}


//--------------------------------------------------------------

void AppDatabaseTest::setup(){
	_myDoFilter = 0;
	_myDoSave = 0;
	
	/* List dir */
	DIR.setVerbose(false);
    nImages = DIR.listDir("input");
	
	_myImageCounter = 2000;
	
	/* Open Database */
	
	_myDatabase.openDB();
	
	/* ---------DATABASE TESTS */
	
	// _myDatabase.searchPictureBySize(3500,2000,1000);
		
	KlausImage* testImage = new KlausImage("pic11.jpg",9000,5000);
	VanishingPoint* testVP = new VanishingPoint();
	testVP->x = 300;
	testVP->y = 400.5;
	testVP->databaseID = 27;
	
	
	
	
	//ImageSegment* testImageSegment = new ImageSegment("segment222.jpg",5000.5,-2000.55,800,200);
	//_myDatabase.saveImage(testImage);
	//_myDatabase.saveVanishingPoint(testImage,testVP);
	//_myDatabase.saveImageSegment(testImageSegment,testVP);
	//std::vector<ImageSegment>* testImageVector = _myDatabase.getSegmentsOfImage(testImage);
	//_myDatabase.setImageState(testImage,2);
	KlausImage* testImage2 = _myDatabase.getImageFromId(9);
	delete testImage;
	delete testVP;
	//delete testImageSegment;
	
}

//--------------------------------------------------------------
void AppDatabaseTest::update(){
}


//--------------------------------------------------------------


void AppDatabaseTest::draw(){
	using namespace abliefern;

	float myScale = 0.2;
	
	float myXOffset = 3400;
	float myYOffset = 1500;
	
	_myDoFilter = 1;
	
	ofBackground(0, 0, 0);
	
	
	if(_myImageCounter < nImages){
		
		
		/* Load image */
		ofImage* myOriginal = new ofImage;
		myOriginal->loadImage(DIR.getPath(_myImageCounter));
		printf("*** Loaded image: %d x %d \n",  myOriginal->width, myOriginal->height);
		std::cout <<  DIR.getName(_myImageCounter) << "\n";
		
		
		
		/* Conver ofImage to ofCV type */
		ofxCvColorImage* myImage = new ofxCvColorImage;
		myImage->allocate(myOriginal->width, myOriginal->height);
		myImage->setFromPixels(myOriginal->getPixels(), myOriginal->width, myOriginal->height);
		
		IplImage* myFullImage = cvCreateImage(cvGetSize(myImage->getCvImage()), IPL_DEPTH_8U, 4);
		
#ifdef DO_BLUR
		IplImage* myBlurImage =  cvCreateImage(cvGetSize(myImage->getCvImage()), IPL_DEPTH_8U, 3);
		cvSmooth(myImage->getCvImage(), myBlurImage, CV_GAUSSIAN);
		
		cvCvtColor(myBlurImage, myFullImage, CV_RGB2RGBA);
		cvReleaseImage(&myBlurImage);

#else
		cvCvtColor(myImage->getCvImage(), myFullImage, CV_RGB2RGBA);
#endif
		
		
		/* Delete useless conversion shit */
		delete myImage;
		
		
		printf("IplImage has %d channels and is %d x %d \n", myFullImage->nChannels, myFullImage->width, myFullImage->height);
		
		
		
		
		/* Canny */
		IplImage* myEdgeImage = cvCreateImage(cvGetSize(myFullImage), IPL_DEPTH_8U, 1);
		
#ifdef RGB_CANNY
		
		cvSetImageCOI(myFullImage, 1);
		IplImage* color1 = cvCreateImage(cvGetSize(myFullImage), IPL_DEPTH_8U, 1);
		IplImage* colore1 = cvCreateImage(cvGetSize(myFullImage), IPL_DEPTH_8U, 1);
		cvCopy(myFullImage, color1);
		cvCanny(color1, colore1, 60, 180, 3);
		
		
		cvSetImageCOI(myFullImage, 2);
		IplImage* color2 = cvCreateImage(cvGetSize(myFullImage), IPL_DEPTH_8U, 1);
		IplImage* colore2 = cvCreateImage(cvGetSize(myFullImage), IPL_DEPTH_8U, 1);
		cvCopy(myFullImage, color2);
		cvCanny(color2, colore2, 60, 180,  3);
		
		
		cvSetImageCOI(myFullImage, 3);
		IplImage* color3 = cvCreateImage(cvGetSize(myFullImage), IPL_DEPTH_8U, 1);
		IplImage* colore3 = cvCreateImage(cvGetSize(myFullImage), IPL_DEPTH_8U, 1);
		
		cvCopy(myFullImage, color3);
		cvCanny(color3, colore3,60, 180, 3);
		
		
		IplImage* result1 = cvCreateImage(cvGetSize(myFullImage), IPL_DEPTH_8U, 1);
		
		
		cvOr(colore1, colore2, result1);
		cvOr(colore2, result1, myEdgeImage);
		
		cvReleaseImage(&color1);
		cvReleaseImage(&color2);
		cvReleaseImage(&color3);
		
		cvReleaseImage(&colore1);
		cvReleaseImage(&colore2);
		cvReleaseImage(&colore3);
		
		cvReleaseImage(&result1);
		
		
		
#else
		/* Convert to grayscale for edge detection */
		cvCvtColor(myFullImage, myGrayImage, CV_RGBA2GRAY);
		
		IplImage* myGrayImage = cvCreateImage(cvGetSize(myFullImage), IPL_DEPTH_8U, 1);
		
		
		/* Perform edge detection */
		cvCanny(myGrayImage, myEdgeImage, 50, 150, 3);
		
		cvReleaseImage(&myGrayImage);
		
#endif		
		
		
		
		
		
		
		/* Hough transform */
		CvMemStorage* myStorage = cvCreateMemStorage(0);
		
		
		
		CvSeq* mySequence = cvHoughLines2(myEdgeImage, myStorage, CV_HOUGH_PROBABILISTIC, 1, CV_PI/180, 100, 60, 1 );
		printf("Found %d lines \n", mySequence->total);
		
		/* Create openframeworks image for saving */
		
		ofImage* mySaveImage = new ofImage;
		mySaveImage->setFromPixels((unsigned char*)myFullImage->imageData, myFullImage->width, myFullImage->height, OF_IMAGE_COLOR_ALPHA, TRUE);
		
		
		//mySaveImage->saveImage("foomanchu.png");
		
		
		/* Drawing stuff */
		ofSetColor(255,255,255);

		mySaveImage->draw(myXOffset*myScale,myYOffset*myScale, mySaveImage->width * myScale, mySaveImage->height*myScale);
		
				
		/* Store Lines */
		vector<Segment*>* myLines = new vector<Segment*>;
		
		for(int i = 0; i < mySequence->total; i++){

			int* myCvLine = (int*) cvGetSeqElem( mySequence , i );
			
			int myX1 = myCvLine[0] ; // first int of first cvPoint struct
			int myY1 = myCvLine[1] ;
			int myX2 = myCvLine[2] ; // first int of second cvPoint struct
			int myY2 = myCvLine[3] ;
			
			
			/* Store line object */
			Segment* myLine = new Segment(myX1,myY1,myX2,myY2);
			
			
			if(filterLine(myLine->angle()) > 0 ){
			   
			myLines->push_back(myLine);	
			   } else {
			   delete myLine;
			   }
			   
			   
		}
		
			
		
		/* Intersect (RANSAC) */
		vector<VanishingPoint*>* myVPs = new vector<VanishingPoint*>;
		
		for(int i = 0; i < myLines->size(); i++){ // for all lines
			Segment* mySegment = myLines->at(i);
			
			for(int j = 0; j < myLines->size(); j++){ // find one intersection
				if(j != i){
					Segment* mySecondSegment = myLines->at(j);
				
					Point2f* myIntersection = mySegment->intersect(mySecondSegment);
					
					if(myIntersection != NULL){ // lines intersect
						VanishingPoint* myVP = new VanishingPoint;
						myVP->x = myIntersection->x;
						myVP->y = myIntersection->y;
						std::cout << "ho" << "\n";
	

						delete myIntersection;
						
						
						for(int k = 0; k < myLines->size(); k++){ // determine lines supporting
							if(k != j && k != i ){
								Segment* myThirdSegment = myLines->at(k);
								
								if(myThirdSegment->distanceToPoint(myVP) < MAX_DISTANCE){
									myVP->supportCount++;
								}
							}
						}
						
						//if(myVP->supportCount > 1){
							myVPs->push_back(myVP);
						//} else {
						//	delete myVP;
						//}
						
					} else { // lines are parallel
						std::cout << "fuck" << "\n";
					}
	
					
				}
			}
			
		}
		ofSetColor(0, 128, 0);
		
		
		/* Draw lines */
		for(int i = 0; i < myLines->size(); i++){
			Segment* myLine = myLines->at(i);
			
			float diffX = myLine->x1 - myLine->x2;
			float diffY = myLine->y1 - myLine->y2;
			
			
			float x1 = myLine->x2 + diffX  *40;
			float y1 = myLine->y2 + diffY * 40;
			
			
			float x0 = myLine->x1 - diffX * 40;
			float y0 = myLine->y1 - diffY * 40;
			
			
			ofLine((x0 +myXOffset)*myScale, (y0 +myYOffset)*myScale, (x1 +myXOffset)*myScale, (y1 +myYOffset)*myScale);
			
			
		}
		
		ofSetColor(255, 0, 0);

		
		
		/* Sort */
		std::sort(myVPs->begin(), myVPs->end(), compareVP);
				
		for(int i= 0; i < MIN(5, myVPs->size()); i++){
			VanishingPoint* myVP = myVPs->at(i);
			ofCircle(myScale* (myVP->x + myXOffset), myScale*(myVP->y + myYOffset), 2);
			printf("VP has %d", myVP->supportCount);
		}
		
		/* Vector cleanup */
		for(int i = 0; i < myLines->size(); i++){

			delete myLines->at(i);	
		}
		
		myLines->clear();
		delete myLines;
		
		/* Show canny */
		ofxCvGrayscaleImage myResult;
		myResult.allocate(myEdgeImage->width, myEdgeImage->height);
		myResult = myEdgeImage;
		
		
		//myResult.draw(0,0,myResult.width, myResult.height);
		
		
		
		ofImage* myGrabImage = new ofImage;
		
		myGrabImage->grabScreen(0, 0, ofGetWidth(), ofGetHeight());
		
		
		string prefix( "output/");
		
		
		
		
		string suffix( ".segments.png");
		string result;
		
		result = prefix + DIR.getName(_myImageCounter) + suffix;
		
		myGrabImage->saveImage(result);
		
		delete myGrabImage;
		
		
		
		
		
		
		
		/* Free OpenCV allocations */
		cvReleaseImage(&myEdgeImage);
		cvReleaseImage(&myFullImage);
		
		delete mySaveImage;
		delete myOriginal;
		
		
		
		
		
		cvReleaseMemStorage(&myStorage);
		
		
		
		
		_myImageCounter++;
	}
	
	ofSleepMillis(1000);
	
	
}

double AppDatabaseTest::modulus(double a, double b)
{
	int result = static_cast<int>( a / b );
	return a - static_cast<double>( result ) * b;
}



int AppDatabaseTest::filterLine(double theTheta){
	
	int myDoDraw = 1;
	double myTolerance = 0.1;
	
	
	// Atan2 ranges [-PI, PI]
	
	float myFlippedTheta = theTheta + CV_PI;
	//if(_myDoFilter){
		if(myFlippedTheta + myTolerance > 0 && myFlippedTheta  - myTolerance <  0 ) myDoDraw = 0;
		if(myFlippedTheta + myTolerance > CV_PI*0.5 && myFlippedTheta  - myTolerance < CV_PI*0.5) myDoDraw = 0;
		if(myFlippedTheta + myTolerance > CV_PI && myFlippedTheta  - myTolerance < CV_PI) myDoDraw = 0;
		if(myFlippedTheta + myTolerance > CV_PI*1.5 && myFlippedTheta  - myTolerance <  CV_PI*1.5 ) myDoDraw = 0;		
		if(myFlippedTheta + myTolerance > CV_PI*2.0 && myFlippedTheta  - myTolerance <  CV_PI*2.0 ) myDoDraw = 0;		

	//}
	
	return myDoDraw;
}


//--------------------------------------------------------------
void AppDatabaseTest::keyPressed  (int key){
}

//--------------------------------------------------------------
void AppDatabaseTest::keyReleased(int key){
	
}

//--------------------------------------------------------------
void AppDatabaseTest::mouseMoved(int x, int y ){
	
}

//--------------------------------------------------------------
void AppDatabaseTest::mouseDragged(int x, int y, int button){
	
}

//--------------------------------------------------------------
void AppDatabaseTest::mousePressed(int x, int y, int button){
	
}

//--------------------------------------------------------------
void AppDatabaseTest::mouseReleased(int x, int y, int button){
	
}

//--------------------------------------------------------------
void AppDatabaseTest::resized(int w, int h){
	
}

